package patch

import (
	"bufio"
	"bytes"
	"embed"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strings"
	"syscall"

	unarr "github.com/gen2brain/go-unarr"

	"gitee.com/StarCitizen_CN/logger"
	"gitee.com/StarCitizen_CN/stargox/pkg/core"
	"gitee.com/StarCitizen_CN/stargox/pkg/fstools"
	"gitee.com/StarCitizen_CN/stargox/pkg/gitee"
)

var (
	//go:embed ATTCH_FILES
	Patch      embed.FS
	AttachPath string
)

const (
	AttchFilesName = "ATTCH_FILES"
)

func Patching() (ok bool, err error) {
	defer func() {
		Patch = embed.FS{}
	}()

	patchDirEntrys, err := Patch.ReadDir(AttchFilesName)
	if err != nil {
		if os.IsNotExist(err) {
			return false, nil
		}
		return false, err
	}

	// 找到 patch_meta
	// 如果它不存在，说明没有更新。
	var patchMeta string
	data, readPatchErr := Patch.ReadFile(AttchFilesName + "/" + PatchMetaFile)
	if os.IsNotExist(readPatchErr) {
		readPatchErr = fmt.Errorf("embed: %w", readPatchErr)
		logger.Err(fmt.Errorf("embed: %w", readPatchErr))
		return false, nil
	}
	if readPatchErr != nil {
		return false, readPatchErr
	}

	patchMeta = strings.TrimSpace(string(data))
	gameRoot, _ := os.Getwd()

	workdir := filepath.Join(core.TempDir, "l10napp")
	AttachPath = filepath.Join(workdir, AttchFilesName)
	patchDir := filepath.Join(AttachPath, "patch")
	patchMetaFilePath := filepath.Join(AttachPath, patchMeta)

	logger.Var(AttachPath)

PATCH_CHECK:
	// checkFIle 不存在则重写缓存
	patchMetaFileInfo, patchMetaFileStatErr := os.Stat(patchMetaFilePath)
	if os.IsNotExist(patchMetaFileStatErr) {
		logger.Var("没有找到补丁")
		_ = os.RemoveAll(AttachPath)
		for _, entry := range patchDirEntrys {

			// 非必要不写入（镜像地址不入源码，其代码详见其他仓库）
			if entry.Name() == PatchMetaFile || entry.Name() == MirrorsFile || entry.Name() == PatchDeleteListFile {
				continue
			}

			logger.Var("从 embed 写入 %s", entry.Name())
			// 从 embed 写入磁盘
			if err := core.WriteEmbedEntrysTo(&Patch, entry, workdir, AttchFilesName); err != nil {
				// 确保无补丁生效
				_ = os.Remove(patchMetaFilePath)
				logger.Err(err)
				return false, err
			}
		}
	} else {

		patchMetaFileAttr := patchMetaFileInfo.Sys().(*syscall.Win32FileAttributeData)
		exePath := os.Args[0]
		exeFileInfo, checkExeFileErr := os.Stat(exePath)
		if checkExeFileErr != nil {
			logger.Err(checkExeFileErr)

			// 重置补丁
			os.Remove(patchMetaFilePath)
			goto PATCH_CHECK
		}
		exeFileAttr := exeFileInfo.Sys().(*syscall.Win32FileAttributeData)

		// 判断是否是重复安装了旧覆盖包
		// 当执行文件比patch更新，则需要重新安装补丁。
		if exeFileAttr.CreationTime.Nanoseconds() > patchMetaFileAttr.CreationTime.Nanoseconds() {
			logger.Var("发现重复安装覆盖包: %s", patchMetaFileStatErr)
			// 重置补丁
			os.Remove(patchMetaFilePath)
			goto PATCH_CHECK
		}

		logger.Var("补丁checkFile状态: %s", patchMetaFileStatErr)
		return false, nil
	}

	if unpackinErr := unpacking(AttachPath); unpackinErr != nil {
		return false, unpackinErr
	}

	// 从缓存写入游戏目录
	patchDirFiles, patchDirReadErr := os.ReadDir(patchDir)
	if patchDirReadErr != nil {
		if os.IsNotExist(patchDirReadErr) {
			return false, nil
		}
		return false, patchDirReadErr
	}
	for _, file := range patchDirFiles {
		dst := gameRoot
		if e := fstools.CopyTo(filepath.Join(patchDir, file.Name()), dst); e != nil {
			// 当出现问题，确保无补丁生效
			_ = os.Remove(patchMetaFilePath)
			return false, e
		}
	}
	// 最后写入 patch_meta
	_ = os.WriteFile(patchMetaFilePath, []byte(patchMeta), os.ModeAppend)
	return true, nil
}

func ReadGiteeMirrors() []string {
	data, readErr := Patch.ReadFile("mirrors.txt")
	if readErr != nil {
		return nil
	}

	dataR := bytes.NewReader(data)
	bs := bufio.NewScanner(dataR)
	bs.Split(bufio.ScanLines)

	mirrors := []string{}
	for bs.Scan() {
		mirror := bs.Text()
		mirror = strings.TrimSpace(mirror)
		if mirror != "" {
			mirrors = append(mirrors, mirror)
		}
	}

	return mirrors
}

func unpacking(targetDir string) (err error) {
	patchArchiveInEmbedFile, openPackErr := Patch.Open(AttchFilesName + "/" + "patch.7z")
	if openPackErr != nil {
		if os.IsNotExist(openPackErr) {
			return nil
		}
		return openPackErr
	}
	defer patchArchiveInEmbedFile.Close()

	patchArchivePath := filepath.Join(targetDir, "patch.7z")
	patchDirPath := filepath.Join(targetDir, "patch")
	os.Remove(patchArchivePath)
	os.RemoveAll(patchDirPath)

	patchArchiveFile, createPatchArchiveErr := os.Create(patchArchivePath)
	if createPatchArchiveErr != nil {
		return createPatchArchiveErr
	}

	if _, copyErr := io.Copy(patchArchiveFile, patchArchiveInEmbedFile); copyErr != nil {
		return copyErr
	}

	a, err := unarr.NewArchive(patchArchivePath)
	if err != nil {
		return err
	}
	defer a.Close()

	_, err = a.Extract(targetDir)
	if err != nil {
		return err
	}

	return nil
}

// todo: 当前只支持一个 patch 下载
func DownloadPatchs(assets *gitee.GiteeReleaseAssets) {

}
